Skip to content

fix(parse): reject incomplete format matches - #152

Open
awais-ur-rehman wants to merge 1 commit into
knowledgecode:masterfrom
awais-ur-rehman:feat/reject-incomplete-date-input
Open

awais-ur-rehman wants to merge 1 commit into
knowledgecode:masterfrom
awais-ur-rehman:feat/reject-incomplete-date-input

Conversation

@awais-ur-rehman

Copy link
Copy Markdown

Summary

Parsing 2025 against YYYY-MM-DD currently succeeds even though the month and day are absent. Record required-token matching failures in preparse and reject them in the shared validator used by parse and isValid, while preserving parsed components and intentional short formats or ellipsis.

PreparseResult gains an optional _error: true field on mismatches; successful result shapes are unchanged.

Breaking changes

None. PreparseResult adds an optional error flag on failed matches.

Testing

Added 20 regression cases, with nine cases failing before the fix. Full npm test passes all 2373 tests after rebuilding; npm run lint, npm run ts, npm run build, and git diff --check pass on Node 24/macOS. Other runtime/platform combinations were not exercised locally.

Manual check: isValid("2025", "YYYY-MM-DD") returns false and parse returns an invalid Date; the same input with YYYY remains valid.

AI assistance

OpenAI Codex assisted with investigation, implementation, regression tests, review, and validation.

@knowledgecode
knowledgecode self-requested a review November 3, 2026 08:03

@knowledgecode knowledgecode left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR! I agree this is a bug. I'll handle docs/ and skills/ myself after the merge.

A few changes before merging:

  1. Rename _error to _mismatch. preparse() doesn't decide validity. The flag records a matching result, like _match.

  2. Set the flag in one place. Wrap the loop in an inner function that returns whether the whole pattern was matched: each break becomes return false, except at ..., which returns true. This way, a branch added later can't forget to set it.

  3. Keep the old _index for a wildcard past the end of the input. With the str.length > 0 guard, preparse('2025-01-01', 'YYYY-MM-DD ') returns _index: 10, _length: 10. On master it returns 11, 10, so code that checks _index === _length itself would start accepting this input. Advance _index as before, then stop the loop.

    const matchPattern = () => {
      for (const token of pattern) {
        // ...
        } else if (token === str[0] || token === wildcard) {
          pr._index++;
          if (!str) {
            return false;
          }
        } // ...
      }
      return true;
    };
    
    if (!matchPattern()) {
      pr._mismatch = true;
    }

    I checked this sketch against all 19 cases in your test file, and they all give the results you expect.

  4. Move the tests into tests/preparse.spec.ts, tests/isValid.spec.ts, and tests/parse.spec.ts. Test files mirror src/.

One note: this isn't quite free of breaking changes. Input that used to parse, such as parse('2025-01', 'YYYY-MM-DD'), now returns Invalid Date. No need to update the description, though.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants